First we take care of the main window, and the application start and end.
.data mainwnd dd 0 wc WNDCLASSEX <size WNDCLASSEX,CS_HREDRAW+CS_VREDRAW,WndProc,0,0, 0, \ 0,0,COLOR_WINDOW+1, 0,wndclsname,0> mainbox CREATEARGS <0,wndclsname,caption,WS_OVERLAPPEDWINDOW+WS_VISIBLE,\ 40,40,300,110,0,0,0,0> wndclsname db 'generic',0 caption db 'Caption (title bar) styles',0 public InitApp,EndApp extrn LoadCursor:near .code InitApp: mov edi,offset wc mov esi,offset mainbox push large IDC_ARROW push large 0 call LoadCursor mov [wc].wcxCursor,eax mov [wc2].wcxCursor,eax ret ; ; Application cleanup subroutine ; Returns: ; EAX = application exit code ; EndApp: xor eax,eax ; assume no errors retNext we define our test window. We explicitly add in the WS_CAPTION because we will use a copy of this style argument as a basis for style changes. It turns out that although an overlapped windows always creates a caption (title bar), the title bar can be removed after the window is created by changing the window style.
.data align 4 testwnd dd 0 wc2 WNDCLASSEX <size WNDCLASSEX,CS_HREDRAW+CS_VREDRAW,DefWindowProc,0,0, \ 0, 0,0,COLOR_WINDOW+1, 0,wndclsname2,0> test CREATEARGS <0,wndclsname2,caption2,WS_OVERLAPPED+WS_CAPTION+\ WS_VISIBLE, 160,160,200,200,0,0,0,0> wndclsname2 db 'testdisplay',0 caption2 db 'Display test',0Now we define the check boxes, which are defined as buttons. There is no WNDCLASSEX structure because the button class is already defined and registered. We will handle all of the important check box activity in the main window, which is the parent (and container) of all these check boxes.
align 4 test_style dd 0 ; Button control IDs IDCTL_SYSMENU equ 101 IDCTL_SIZEBOX equ 102 IDCTL_MINBOX equ 103 IDCTL_MAXBOX equ 104 ; Button control chkbox CREATEARGS <0,btnclsname,0,WS_CHILD+WS_VISIBLE+BS_CHECKBOX,\ 0,0,0,0, 0,0,0,0> btnclsname db 'button',0 ; letter case doesn't matter ; Button style/initialization tables align 4 sysmenu dd WS_SYSMENU,sysmenu_str,10,10,250,15,IDCTL_SYSMENU sizebox dd WS_THICKFRAME,sizebox_str,10,25,250,15,IDCTL_SIZEBOX minbox dd WS_MINIMIZEBOX,minbox_str,10,40,250,15,IDCTL_MINBOX maxbox dd WS_MAXIMIZEBOX,maxbox_str,10,55,250,15,IDCTL_MAXBOX sysmenu_str db 'WS_SYSMENU',0 sizebox_str db 'WS_THICKFRAME/WM_SIZEBOX',0 minbox_str db 'WS_MINIMIZEBOX',0 maxbox_str db 'WS_MAXIMIZEBOX',0There is only one window procedure defined by us, and that is the one for the main window, the one with the check boxes in it. We start with the message dispatch.
extrn DefWindowProc:near .code WndProc: mov eax,[esp+4+4] ; message ID cmp eax,WM_COMMAND ; controls clicked je execute_command cmp eax,WM_CREATE ; window created je finish_create cmp eax,WM_DESTROY ; about to start window destruction je start_destroy jmp DefWindowProc ; delegate other message processingWe create the check boxes here, as a response to WM_CREATE. At this time, the main window has been created, and there is a window handle for it. The handle is used to assign the window as parent to each check box, which are created as a child window. A child window must be assigned a parent when it is created.
extrn CreateWindowEx:near extrn RegisterClassEx:near finish_create: mov eax,[esp+4+0] ; hwnd mov [mainwnd],eax mov eax,[wc].wcxInstance mov [wc2].wcxInstance,eax push offset wc2 call RegisterClassEx push esi push edi mov esi,offset test mov eax,[esi].cwargStyle mov [test_style],eax ; save original style call install_subwindow mov [testwnd],eax mov esi,offset chkbox mov edi,offset sysmenu call install_chkbox mov esi,offset chkbox mov edi,offset sizebox call install_chkbox mov esi,offset chkbox mov edi,offset minbox call install_chkbox mov esi,offset chkbox mov edi,offset maxbox call install_chkbox pop edi pop esi xor eax,eax ; signal a successful CREATE ret 16 ; ; Create chkbox ; ; ESI = address of CreateWindowEx arguments ; EDI = chkbox table entry ; ; Returns: ; ; EAX = BUTTON handle ; install_chkbox: mov eax,4[edi] mov [chkbox].cwargName,eax ; set window name = window text mov eax,8[edi] mov [chkbox].cwargX,eax mov eax,12[edi] mov [chkbox].cwargY,eax mov eax,16[edi] mov [chkbox].cwargCx,eax mov eax,20[edi] mov [chkbox].cwargCy,eax mov eax,24[edi] mov [chkbox].cwargMenu,eax ; ctl ID when WS_CHILD call install_subwindow ret ; ; Create main window's subwindows ; ; ESI = address of CreateWindowEx arguments ; ; Returns: ; ; EAX = handle of subwindow ; install_subwindow: mov eax,[mainwnd] mov [esi].cwargParent,eax ; make "mainbox" owner or parent ; of new window mov eax,[wc].wcxInstance ; get instance mov [esi].cwargInstance,eax ; set instance of window class sub esp,48 ; allocate args mov edi,esp mov ecx,12 rep movsd call CreateWindowEx ret ; ; Process WM_DESTROY. Sent after window is removed from screen, but ; before any destruction begins. ; extrn PostQuitMessage:near start_destroy: push large 0 call PostQuitMessage xor eax,eax ret 16This is where most of the work happens, in response to WM_COMMAND. If the command is not a button (check box) command, we quit. Otherwise we fetch the style option. Because the option is encoded in a single bit, the option doubles as a bit mask.
extrn SendMessage:near extrn SetWindowLong:near extrn RedrawWindow:near execute_command: mov eax,[esp+4+8] ; wParam mov edx,[esp+4+12] ; lParam cmp eax,(BN_CLICKED shl 16)+IDCTL_SYSMENU je select_sysmenu cmp eax,(BN_CLICKED shl 16)+IDCTL_SIZEBOX je select_sizebox cmp eax,(BN_CLICKED shl 16)+IDCTL_MINBOX je select_minbox cmp eax,(BN_CLICKED shl 16)+IDCTL_MAXBOX je select_maxbox jmp exit_execute_command ; ; Get styles from table ; select_sysmenu: mov ecx,[sysmenu] jmp toggle select_sizebox: mov ecx,[sizebox] jmp toggle select_minbox: mov ecx,[minbox] jmp toggle select_maxbox: mov ecx,[maxbox] jmp toggleNow we get and reset the check box state. Here we see that messages are the main means of transferring information to and from the controls.
toggle: push ecx ; save style push edx ; save ctl handle push large 0 push large 0 push large BM_GETCHECK push edx ; ctl handle from lParam call SendMessage pop edx ; retrieve ctl handle xor eax,1 ; toggle check state push large 0 push eax ; new check state push large BM_SETCHECK push edx ; ctl handle call SendMessage pop ecx ; retrieve styleThen we change the window style of our test window.
xor [test_style],ecx ; toggle style bit push [test_style] ; new style push large GWL_STYLE push [testwnd] ; test window call SetWindowLongChanging the window style does not cause a redrawing of the window. Without the following, the test window doesn't change until it is selected. And the redraw is imperfect.
push large (RDW_FRAME+RDW_INVALIDATE+RDW_UPDATENOW) push large 0 ; update rectangle push large 0 ; update region (overrides rect) push [testwnd] ; test window call RedrawWindow exit_execute_command: xor eax,eax ; signal WM_COMMAND was processed ret 16